﻿#include <bperutil/sys/BPerWebsocket.h>

#include <libwebsockets.h>
#include <bperutil/BPerType.h>
#include <bperutil/sys/BPerLog.h>
#include <bperutil/sys/BPerFile.h>
#include <bperutil/sys/BPerVector.h>
#include <bperutil/sys/BPerLibHandle.h>

#include <bperutil/sys/BPerThread.h>
#if BPer_Enable_Websockets
typedef struct{
	void (*bPer_lws_set_log_level)(int level, lws_log_emit_t log_emit_function);
    int	(*bPer_lws_write)(struct lws *wsi, unsigned char *buf, size_t len, enum lws_write_protocol protocol);
	int	(*bPer_lws_callback_on_writable)(struct lws *wsi);
	int	(*bPer_lws_http_client_read)(struct lws *wsi, char **buf, int *len);
	int (*bPer_lws_add_http_header_by_token)(struct lws *wsi, enum lws_token_indexes token,
				     const unsigned char *value, int length,
				     unsigned char **p, unsigned char *end);
	void (*bPer_lws_client_http_body_pending)(struct lws *wsi, int something_left_to_send);
	struct lws_context* (*bPer_lws_create_context)(const struct lws_context_creation_info *info);

	struct lws* (*bPer_lws_client_connect_via_info)(const struct lws_client_connect_info *ccinfo);
	void (*bPer_lws_context_destroy)(struct lws_context *context);
	int	(*bPer_lws_service)(struct lws_context *context, int timeout_ms);
	void (*bPer_lws_cancel_service)(struct lws_context *context);
}BPerLws;



typedef struct{
	BPerIpFamilyType familyType;
	int32_t isloop;
	int32_t isStart;
	bPer_thread_t threadId;

    BPerBool ssl;
	BPerWebsocketCallback* callback;
    BPerUrlData urlData;
    struct lws_context *wsicontext;
	struct lws *wsi;
	struct lws_context_creation_info info;
	struct lws_client_connect_info conn_info;

	BPerLws lws;
    BPerLibHandle lib;
}BPerWebsocketContext;



int bPer_websocket_callback(struct lws *wsi, enum lws_callback_reasons reason,
		void *user, void *in, size_t len) {
	if (user == NULL)
		return 0;
	BPerWebsocketContext *obj = (BPerWebsocketContext*) user;

	if(reason==LWS_CALLBACK_CLIENT_RECEIVE){
    // 接收到服务器数据后的回调，数据为in，其长度为len



		BPerSample pdata;
		pdata.bytes = (char*) in;
		pdata.nb = len;

		if (obj->callback)	obj->callback->receive(obj->callback->context, &pdata);
		return BPer_Ok;
	}
	if(reason==LWS_CALLBACK_CLIENT_ESTABLISHED){
		bPer_info("Connected to server ok!\n");
	}
	/**switch (reason) {
	case LWS_CALLBACK_CLIENT_ESTABLISHED:   // 连接到服务器后的回调
		bPer_info("Connected to server ok!\n");
		break;
	case LWS_CALLBACK_CLIENT_RECEIVE:       // 接收到服务器数据后的回调，数据为in，其长度为len
	{
		//bPer_trace("Rx: %s\n", (char* ) in);

		BPerSample pdata;
		pdata.bytes = (char*) in;
		pdata.nb = len;

		if (obj->callback)	obj->callback->receive(obj->callback->context, &pdata);

		break;
	}
	case LWS_CALLBACK_CLIENT_WRITEABLE:     // 当此客户端可以发送数据时的回调
	{
		break;
	}

	}**/
	return BPer_Ok;
}


void bPer_websocket_loadLib(void* pcontext){
	if(pcontext==NULL) return;
		BPerWebsocketContext* context=(BPerWebsocketContext*)pcontext;
		bPer_create_libhandle(&context->lib);
		BPerLibHandle* lib=&context->lib;
        lib->loadObject(lib->context,"libwebsockets");
        context->lws.bPer_lws_set_log_level=(void (*)(int level, lws_log_emit_t log_emit_function))lib->loadFunction(lib->context,"lws_set_log_level");
        context->lws.bPer_lws_create_context=(struct lws_context * (*)(const struct lws_context_creation_info *info))lib->loadFunction(lib->context,"lws_create_context");
        context->lws.bPer_lws_write=(int	(*)(struct lws *wsi, unsigned char *buf, size_t len, enum lws_write_protocol protocol))lib->loadFunction(lib->context,"lws_write");
        context->lws.bPer_lws_client_connect_via_info=(struct lws* (*)(const struct lws_client_connect_info *ccinfo))lib->loadFunction(lib->context,"lws_client_connect_via_info");
        context->lws.bPer_lws_context_destroy=(void (*)(struct lws_context *context))lib->loadFunction(lib->context,"lws_context_destroy");
        context->lws.bPer_lws_service=(int	(*)(struct lws_context *context, int timeout_ms))lib->loadFunction(lib->context,"lws_service");
        context->lws.bPer_lws_cancel_service=(void (*)(struct lws_context *context))lib->loadFunction(lib->context,"lws_cancel_service");
        context->lws.bPer_lws_callback_on_writable=(int	(*)(struct lws *wsi))lib->loadFunction(lib->context,"lws_callback_on_writable");
        context->lws.bPer_lws_http_client_read=(int	(*)(struct lws *wsi, char **buf, int *len))lib->loadFunction(lib->context,"lws_http_client_read");
		context->lws.bPer_lws_add_http_header_by_token=(int (*)(struct lws *wsi, enum lws_token_indexes token,
				     const unsigned char *value, int length,
                     unsigned char **p, unsigned char *end))lib->loadFunction(lib->context,"lws_add_http_header_by_token");
        context->lws.bPer_lws_client_http_body_pending=(void (*)(struct lws *wsi, int something_left_to_send))lib->loadFunction(lib->context,"lws_client_http_body_pending");
}

void bPer_websocket_unloadLib(void* pcontext){
	if(pcontext==NULL) return;
	BPerWebsocketContext* context=(BPerWebsocketContext*)pcontext;
	bPer_destroy_libhandle(&context->lib);
}
int bPer_websocket_init(void* pcontext) {
	if(pcontext==NULL) return 1;
	BPerWebsocketContext* context=(BPerWebsocketContext*)pcontext;
	//bool ssl = protocol == "wss" ? true : false; //确认是否进行SSL加密
    context->lws.bPer_lws_set_log_level(7, NULL);


	bPer_memset(&context->info, 0, sizeof(context->info));
	/** const struct lws_protocols protocols[] = {
	                {"ws",bPer_websocket_callback,0,2048
	                },

	                {NULL, NULL, 0, 0}};**/
	static const struct lws_protocols protocols[] = {
		{
			"ws-client",
			bPer_websocket_callback,
			0, 4096, 0, NULL, 0
		},
		LWS_PROTOCOL_LIST_TERM
	};

	context->info.protocols = protocols;       //设置处理协议
	context->info.port = CONTEXT_PORT_NO_LISTEN; //作为ws客户端，无需绑定端口
	context->info.iface=NULL;
	//ws和wss的初始化配置不同
	context->info.options = context->ssl ? LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT : 0; //如果是wss，需要做全局SSL初始化
	//info.options |= LWS_SERVER_OPTION_H2_JUST_FIX_WINDOW_UPDATE_OVERFLOW;
	context->info.gid = (gid_t)-1;
	context->info.uid = (uid_t)-1;
	if(context->ssl){
		char cerpath[255]={0};
		char privatekey[255]={0};

		bPer_getCaFile(cerpath,privatekey);

		context->info.client_ssl_cert_filepath=cerpath;
		context->info.client_ssl_private_key_filepath=privatekey;
		context->info.client_ssl_private_key_password="metaRTC";
		//info.client_ssl_ca_filepath=cerpath;
	}
    context->wsicontext = context->lws.bPer_lws_create_context(&context->info); //创建连接上下文
    if (context->wsicontext == NULL) {
		bPer_error("create context error\n");
		return 1;
	}

	bPer_memset(&context->conn_info, 0, sizeof(struct lws_client_connect_info));
	 char addr_port[256] = { 0 };
    bPer_sprintf(addr_port, sizeof addr_port,"%s:%u", context->urlData.server, context->urlData.port & 65535 );
	//初始化连接信息
     context->conn_info.context = context->wsicontext;      //设置上下文
	 context->conn_info.address = context->urlData.server; //设置目标主机IP
	 context->conn_info.port = context->urlData.port;            //设置目标主机服务端口
	 context->conn_info.path = "./";//context->urlData.app.c_str();    //设置目标主机服务PATH
	 context->conn_info.host = context->urlData.server;//addr_port;      //设置目标主机IP
	 context->conn_info.origin = context->urlData.server;//addr_port;    //设置目标主机IP
	//conn_info.pwsi = &context->wsi;            //设置wsi句柄
	 context->conn_info.userdata =pcontext;//NULL;      //userdata 指针会传递给callback的user参数，一般用作自定义变量传入
	 context->conn_info.protocol = protocols[0].name;
	//conn_info.ietf_version_or_minus_one=-1;
	//conn_info.method="POST";
	//ws/wss需要不同的配置
	int usessl= LCCSCF_USE_SSL |
						  LCCSCF_ALLOW_SELFSIGNED |
						  LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK;
	//LCCSCF_ALLOW_INSECURE)
	context->conn_info.ssl_connection =	context->ssl ? usessl:	0;
	context->wsi = context->lws.bPer_lws_client_connect_via_info(&context->conn_info);

	if(context->wsi==NULL){
		return bPer_error_wrap(1,"lws_client_connect_via_info error!");
	}


	return BPer_Ok;
}



void* bPer_run_websocket_thread(void *obj) {
	BPerWebsocketContext* context=(BPerWebsocketContext*)obj;
	context->isStart=1;
	context->isloop=1;
	while (context->isloop) {
		//lws_service
        context->lws.bPer_lws_service(context->wsicontext, 500);
      //  context->lws.bPer_lws_callback_on_writable(context->wsi);
	}
	context->isStart=0;
	return NULL;
}
void bPer_websocket_start(void* pcontext) {
	if(pcontext==NULL) return;
	BPerWebsocketContext* context=(BPerWebsocketContext*)pcontext;
	if (bPer_thread_create(&context->threadId, 0, bPer_run_websocket_thread, pcontext)) {
		bPer_error("BPerThread::start could not start thread");

	}

}
void bPer_websocket_stop(void* pcontext) {
	if(pcontext==NULL) return;
	BPerWebsocketContext* context=(BPerWebsocketContext*)pcontext;
	context->isloop=0;
	int32_t waitCount=0;
	while (context->isStart)	{
		bPer_usleep(1000);
		waitCount++;
		if(waitCount>500&&context->lws.bPer_lws_cancel_service) context->lws.bPer_lws_cancel_service(context->wsicontext);
	}
}
int32_t bPer_websocket_connectServer(void* pcontext,char* url) {
	if(pcontext==NULL) return 1;
	BPerWebsocketContext* context=(BPerWebsocketContext*)pcontext;
	if(bPer_ws_url_parse(context->familyType,url,&context->urlData)) return 1;
	bPer_trace("\nserver==%s,port==%d,path==%s",context->urlData.server,context->urlData.port,context->urlData.app);
	context->ssl=(context->urlData.netType==BPer_Websocket_Wss||context->urlData.netType==BPer_Websocket_Https)?1:0;
	int err=BPer_Ok;
	if((err=bPer_websocket_init(pcontext))==BPer_Ok) {
		bPer_websocket_start(pcontext);
	}else{
		return bPer_error_wrap(err,"websocket init error!");
	}
	return err;
}
int bPer_websocket_sendData(void* pcontext,uint8_t *p, int nb) {
	if (pcontext == NULL)
		return 1;
	BPerWebsocketContext *context = (BPerWebsocketContext*) pcontext;
	char buf[2000];
	bPer_memset(buf, 0, 2000);
	//LWS_SEND_BUFFER_PRE_PADDING
	bPer_memcpy(buf + LWS_PRE, p, nb);
	//bPer_trace("\nsend data(%d)=%s", nb, buf+LWS_PRE);

	context->lws.bPer_lws_write(context->wsi, ((uint8_t*)buf) + LWS_PRE, nb,LWS_WRITE_TEXT);
	context->lws.bPer_lws_callback_on_writable(context->wsi);

	return BPer_Ok;
}
int32_t bPer_websocket_disconnectServer(void* pcontext) {
	if(pcontext==NULL) return 1;
	bPer_websocket_stop(pcontext);
    BPerWebsocketContext* context=(BPerWebsocketContext*)pcontext;
    if(context->wsicontext) 	context->lws.bPer_lws_context_destroy(context->wsicontext);
    context->wsicontext=NULL;
	return BPer_Ok;
}

void bPer_create_websocket(BPerIpFamilyType familyType,BPerWebsocket* web,BPerWebsocketCallback* callback){
	if(web==NULL) return;
    web->context=bPer_calloc(sizeof(BPerWebsocketContext),1);
    BPerWebsocketContext* context=(BPerWebsocketContext*)web->context;

	context->callback=callback;
    context->wsicontext = NULL;
	context->wsi=NULL;
	context->isloop=0;
	context->familyType=familyType;
	context->ssl= BPerFalse;

    bPer_websocket_loadLib(web->context);
	web->connectServer=bPer_websocket_connectServer;
	web->disconnectServer=bPer_websocket_disconnectServer;
	web->sendData=bPer_websocket_sendData;
}
void bPer_destroy_websocket(BPerWebsocket* web){
	if(web==NULL) return;

    bPer_websocket_disconnectServer(web->context);

    bPer_websocket_unloadLib(web->context);
    bPer_free(web->context);
}
#endif
